# This file is part the GoMAT AUTOSAR viewer which is part of the
# AutoMAT product suite (https://bitbucket.com/mahomaho/AutoMAT).
# Copyright (c) 2021 Mattias Holmqvist.
# 
# AutoMAT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AutoMAT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with AutoMAT.  If not, see <https://www.gnu.org/licenses/>.


from collections import defaultdict
from typing import *
import ctypes
import os
import sys
import platform

from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
import PySide6

from AutoMAT import *
from AutoMAT import _vprint as vprint
from AutoMAT import complexbase, simplebase

undo = []
redo = []

gomatpath=os.path.dirname(__file__)

PadLockIcon=QIcon(os.path.join(gomatpath,'padlock.svg'))
ErrorIcon=QIcon(os.path.join(gomatpath,'error.svg'))
WarnIcon=QIcon(os.path.join(gomatpath,'warning.svg'))
Qt.AddListRole=Qt.UserRole+1
Qt.CreateChildRole = Qt.AddListRole + 1
Qt.AutoMATRole = Qt.CreateChildRole + 1
Qt.DeleteRole = Qt.AutoMATRole + 1
Qt.OkToHideRole = Qt.DeleteRole

class SelectFileDialog(QDialog):
	def __init__(self, files):
		super().__init__(mainWindow)
	
		self.setWindowTitle("Select in which file to store property")
		
		layout = QHBoxLayout(self)
	
		self.combo_box = QComboBox(self)
		layout.addWidget(self.combo_box)
		#self.combo_box.setGeometry(50, 50, 200, 30)
	
		self.combo_box.addItems(files)
	
		button = QPushButton("Select File", self)
		layout.addWidget(button)
		#button.setGeometry(50, 100, 200, 30)
		button.clicked.connect(self.accept)
		self.setLayout(layout)
	
	def get_selected_file(self):
		return self.combo_box.currentText()


class Model(QAbstractItemModel):

	def __init__(self, object):
		super().__init__(object)
		autosar._modelrow = 0
		autosar._modelchildren = list(autosar.aggregations())
	def automat2index(self, automat):
		automat = self.automat2model(automat)
		if automat is None:
			return QModelIndex()
		return self.createIndex(automat._modelrow, 0, automat)
	@staticmethod
	def automat2model(automat):
		if '_modelchildren' in automat.__dict__:
			# this automat is the model
			return automat
		parent = Model.automat2model(automat.parent())
		if not isinstance(automat, complexbase.ContainerBase):
			automat._modelparent = parent
			automat._modelrow = parent._modelchildren.index(automat)
			if automat and automat.upperMultiplicity() == 1:
				automat._modelchildren = list(automat[0].aggregations())
			else:
				automat._modelchildren = list(automat)
			return automat
		binding = automat.binding()
		if binding.upperMultiplicity() == 1:
			# update parent to get the binding
			return Model.automat2model(next(child for child in parent._modelchildren if child == binding))
		automat._modelparent = next(child for child in parent._modelchildren if child == binding)
		Model.automat2model(automat._modelparent)
		automat._modelchildren = list(automat.aggregations())
		automat._modelrow = automat._modelparent._modelchildren.index(automat)
		return automat
	def parent(self,child):
		automat=child.internalPointer()
		if automat is autosar:
			return QModelIndex()
		return self.createIndex(automat._modelparent._modelrow, 0, automat._modelparent)
	def index(self, row, column , parent):
		if not parent.isValid():
			if row != 0:
				return QModelIndex()
			# assert row == 0
			return self.createIndex(row, column, autosar)
		automat = parent.internalPointer()
		if row >= len(automat._modelchildren):
			return QModelIndex()
		return self.createIndex(row, column, self.automat2model(automat._modelchildren[row]))
	def rowCount(self,parent):
		if not parent.isValid():
			return 1
		automat=parent.internalPointer()
		return len(automat._modelchildren)
	def columnCount(self,parent):
		return 4

	def supportedDropActions(self):
		return Qt.CopyAction | Qt.MoveAction
	def supportedDragActions(self)->PySide6.QtCore.Qt.DropActions:
		return Qt.CopyAction | Qt.MoveAction
	def headerData(self,section, orientation,role):
		if role!=Qt.DisplayRole:
			return None
		if orientation!=Qt.Horizontal:
			return None
		if section==0:
			return 'Model element'
		if section==1:
			return 'value'
		if section==2:
			return 'type'
		if section==3:
			return 'file'

	def status(self, automat):
		try:
			automat.validate()
			if automat and isinstance(automat, complexbase.Aggregation) and automat.upperMultiplicity() == 1:
				automat[0].validate()
			return ''
		except AssertionError as e:
			return str(e)
		except Exception as e:
			vprint(f'Unexpected exception in rule file {e.__traceback__.tb_next.tb_frame.f_code.co_filename}:{e.__traceback__.tb_next.tb_frame.f_lineno} for {automat}: {e}')
	def data(self,index, role):
		automat=index.internalPointer()
		if role == Qt.AutoMATRole:
			return automat
		if role in (Qt.WhatsThisRole,Qt.ToolTipRole):
			ret = ''
			if isinstance(automat, complexbase.Aggregation):
				if index.column() <= 1:
					# property/value column
					ret=automat.__doc__
				elif index.column() == 2 and automat.upperMultiplicity() == 1 and automat:
					# type column
					ret=automat[0].__doc__
				else:
					ret = ''
				status=self.status(automat)
			else:
				if index.column() <= 1:
					# property/value column
					ret=automat.binding().__doc__
				elif index.column() == 2:
					# type column
					ret=automat.__doc__
				else:
					ret = ''
				status=self.status(automat)
			if status:
				if ret:
					ret+='\n'
				ret+='Error: '+status
			return ret
		if role==Qt.DecorationRole:
			if index.column()==0:
				if self.status(automat):
					return ErrorIcon
				if (automat.editor().readonly() if isinstance(automat,complexbase.ContainerBase) else automat.parent().editor().readonly()):
					return PadLockIcon
			return None
		if role==Qt.ForegroundRole:
			if not automat:
				return app.palette().color(QPalette.Disabled, QPalette.Text)
			file_name = mainWindow.fileselector.currentText()
			if file_name:
				if not isinstance(automat,complexbase.ContainerBase):
					if automat.upperMultiplicity() > 1:
						for child in automat:
							if file_name in child.file():
								return app.palette().color(QPalette.Highlight)
					else:
						if file_name in automat[0].file():
							return app.palette().color(QPalette.Highlight)
				else:
					if file_name in automat.file():
						return app.palette().color(QPalette.Highlight)
			return None
		if role==Qt.OkToHideRole:
			return not automat and not self.status(automat)
		if role==Qt.DisplayRole:
			if index.column() == 0:
				#if hasattr(e,'shortName'):
				#	return f'{e.shortName}'
				if isinstance(automat, complexbase.ContainerBase):
					binding = automat.binding()
					if binding.sortable():
						if isinstance(automat, autosar_r4p0.Group.Referrable):
							return f'[{automat._modelrow}]: {automat.shortName.val()}'
						else:
							return f'[{automat._modelrow}]'
					if isinstance(automat, autosar_r4p0.Group.Referrable):
						return automat.shortName.val()
					return automat.binding().name()
				else:
					name=automat.name()
					if automat.upperMultiplicity()>1:
						return name + ('s{' if name[-1] != 's' else 'es{') + str(len(automat)) + '}'
					#elif automat and isinstance(automat[0],autosar_r4p0.Group.Referrable):
					#	name = automat[0].shortName.val()
					return name
			if index.column() == 1:
				if isinstance(automat,complexbase.ContainerBase):
					binding=automat.binding()
					if binding.isProperty():
						if issubclass(binding.valuetype(), int):
							return hex(automat.val()) if action_showHex.isChecked() else str(automat.val())
						return str(automat.val())
				elif automat.isProperty():
					if automat and automat.upperMultiplicity() == 1:
						if issubclass(automat.valuetype(), int):
							return hex(automat[0].val()) if action_showHex.isChecked() else str(automat[0].val())
						return str(automat[0].val())
					return '-'
				return None
			if index.column()==2:
				if isinstance(automat, complexbase.ContainerBase):
					return type(automat).__name__
				else:
					desttype=automat.desttype()
					if isinstance(desttype, type) and issubclass(desttype, complexbase.ModelReference):
						refs = list(automat._element._desttype._dests.values())
						if len(refs) == 1:
							return 'ref:'+ refs[0].__name__
						else:
							return 'ref:'+ str([a.__name__ for a in refs])
					if automat and automat.upperMultiplicity() == 1:
						return type(automat[0]).__name__
					else:
						return desttype.__name__ if not isinstance(desttype, list) else str(list(t.__name__ for t in desttype)) if len(desttype) > 1 else desttype[0].__name__
			if index.column()==3:
				files = automat.file_line() if isinstance(automat, complexbase.ContainerBase) else automat.parent().file_line() if not automat or automat.upperMultiplicity() > 1 else automat[0].file_line()
				return str(files if len(files) > 1 else files[0])
		if role==Qt.AddListRole:
			if not isinstance(automat,complexbase.ContainerBase):
				if automat.parent().editor().readonly() or automat.isProperty():
					return None
				if automat.upperMultiplicity() == 1:
					if automat:
						automat = automat[0]
					else:
						# truw means "add" possible
						return True
				else:
					# this is a list root
					desttype=automat.desttype()
					if isinstance(desttype,list):
						if len(desttype) > 1:
							return {t.__name__:None for t in desttype}
						else:
							# only one option is valid, no need to specify type
							return True
					#return {desttype.__name__:None}
					return True
			ret={}
			for child in automat.aggregations():
				if child.isProperty():
					continue
				if child.upperMultiplicity() <= len(child):
					continue
				if not child._element._splitable:
					selectedFile = mainWindow.fileselector.currentText()
					if selectedFile:
						if selectedFile not in automat.file() and automat.binding()._element._splitable:
							continue
				desttype = child.desttype()
				if isinstance(desttype,list):
					if len(desttype)==1:
						desttype=desttype[0]
					else:
						ret[child.name()] = [t.__name__ for t in desttype]
						continue
				ret[child.name()] = None
			return ret
		return None
	def canDropMimeData(self, data:PySide6.QtCore.QMimeData, action:PySide6.QtCore.Qt.DropAction, row:int, column:int, parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->bool:
		return False
	def dropMimeData(self, data:PySide6.QtCore.QMimeData, action:PySide6.QtCore.Qt.DropAction, row:int, column:int, parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->bool:
		return False
	def mimeData(self, indexes)->PySide6.QtCore.QMimeData:
		mimeData = QMimeData()
		data='\n'.join((idx.internalPointer().__repr__() for idx in indexes if idx.column() == 0))
		mimeData.setData("AutoMAT/row/AUTOSAR", QByteArray(data));
		return mimeData;
	def mimeTypes(self)->List[str]:
		return ['AutoMAT/row/AUTOSAR',]
	def flags(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->PySide6.QtCore.Qt.ItemFlags:
		automat=index.internalPointer()
		if automat is None:
			return Qt.NoItemFlags
		ret=Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled|Qt.ItemIsDropEnabled
		if index.column() == 1:
			container = isinstance(automat, complexbase.ContainerBase)
			if container and automat.binding().isProperty() or not container and (automat.isProperty() or automat.desttype().__class__ is list and len(automat.desttype()) > 1):
				ret |= Qt.ItemIsEditable
		return ret
	def setData(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], value:Any, role:int=...)->bool:
		automat=index.internalPointer()
		if role==Qt.EditRole:
			if isinstance(automat, complexbase.ContainerBase):
				automat.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).set(value)
				self.dataChanged.emit(index.siblingAtColumn(0), index.siblingAtColumn(3))
			else:
				assert automat.isProperty()
				if automat.upperMultiplicity() > 1:
					file = mainWindow.fileselector.currentText()
					if not file:
						if len(automat.parent().file()) == 1:
							file = automat.parent().file()[0]
						else:
							file_dialog = SelectFileDialog(automat.parent().file())
							file_dialog.exec_()
							file = file_dialog.get_selected_file()
					child = getattr(automat.parent().editor(file=file), automat.name()).append(value).model()
					self.beginInsertRows(index.siblingAtColumn(0), len(automat._modelchildren), len(automat._modelchildren))
					automat._modelchildren.append(child)
					self.automat2model(child)
					self.endInsertRows()
					self.dataChanged.emit(index.siblingAtColumn(0), index.siblingAtColumn(0))
				else:
					if automat.name() == 'shortName':
						refs = []
						refs.extend(automat.parent().references())
						oldName = automat.parent().__str__()
						def GetReferrable(parent):
							for child in parent.children():
								if isinstance(child, autosar_r4p0.Group.Referrable):
									refs.extend(child.references())
								if isinstance(child,complexbase.ComplexTypeBase):
									GetReferrable(child)
						GetReferrable(automat.parent())
					if not automat:
						file = mainWindow.fileselector.currentText()
						if not file:
							if len(automat.parent().file()) == 1:
								file = automat.parent().file()[0]
							else:
								file_dialog = SelectFileDialog(automat.parent().file())
								file_dialog.exec_()
								file = file_dialog.get_selected_file()
					else:
						file = mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None
					try:
						setattr(automat.parent().editor(file=file), automat.name(), value)
					except ValueError as e:
						print(e)
						return False
					self.dataChanged.emit(index.siblingAtColumn(0), index.siblingAtColumn(3))
					#if automat.desttype() is autosar_r4p0.Identifier:
					if automat.name() == 'shortName':
						# parents sorting may change if changeing shortName
						parent=self.parent(index)
						self.dataChanged.emit(parent.siblingAtColumn(0), parent.siblingAtColumn(3))
						parent=self.parent(parent)
						siblings=parent.internalPointer()
						if siblings.upperMultiplicity() > 1:
							for row, sibling in enumerate(siblings):
								if sibling._modelrow != row:
									self.beginMoveRows(parent, sibling._modelrow, sibling._modelrow, parent, row)
									siblings._modelchildren.remove(sibling)
									siblings._modelchildren.insert(row, sibling)
									for i in range(row, sibling._modelrow + 1):
										siblings._modelchildren[i]._modelrow = i
									self.endMoveRows()
						newName = automat.parent().__str__()
						for ref in refs:
							newVal = newName + ref.val().removeprefix(oldName)
							ref.editor().set(newVal)
							#if hasattr(ref,'_modelparent'):
							idx = self.automat2index(ref).siblingAtColumn(1)
							self.dataChanged.emit(idx, idx)
									
								
			return True
		if role==Qt.CreateChildRole:
			if isinstance(automat, complexbase.ContainerBase):
				if value is None:
					return False
				elif isinstance(value, str):
					automat = next(child for child in automat._modelchildren if child.name() == value)
					index = self.automat2index(automat)
					return self.setData(index, None, Qt.CreateChildRole)
				else:
					automat = next(child for child in automat._modelchildren if child.name() == value[0])
					index = self.automat2index(automat)
					return self.setData(index, value[1], Qt.CreateChildRole)
			else:
				# automat is an aggregation
				if isinstance(value, str):
					# value is a single string
					if automat.upperMultiplicity() == 1:
						if not automat:
							# is this code used? Not sure
							child = getattr(getattr(automat.parent().editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), automat.name()), value).model()
							children = child.aggregations()
							self.beginInsertRows(index.siblingAtColumn(0), 0, len(children) - 1)
							automat._modelchildren.extend(children)
							self.endInsertRows()
						else:
							parent = automat[0]
							#assert automat.upperMultiplicity() == 1
							aggr= next((child for child in automat._modelchildren if child.name()==value))
							index=self.automat2index(aggr)
							if aggr.upperMultiplicity() == 1:
								assert not aggr
								child = getattr(parent.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), value).model()
								children = child.aggregations()
								self.beginInsertRows(index, 0, len(children) - 1)
								aggr._modelchildren.extend(children)
								for idx, c in enumerate(children):
									self.automat2model(c)
									c._modelrow = idx
								self.endInsertRows()
							else:
								child = getattr(parent.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), value).append().model()
								row = aggr.index(child)
								self.beginInsertRows(index, row, row)
								aggr._modelchildren.insert(row, child)
								for idx, c in enumerate(aggr._modelchildren[row:], row):
									self.automat2model(c)
									c._modelrow = idx
								self.endInsertRows()
								
					else:
						child = getattr(getattr(automat.parent().editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), automat.name()), value).append().model()
						row = automat.index(child)
						self.beginInsertRows(index.siblingAtColumn(0), row, row)
						automat._modelchildren.insert(row, child)
						for idx, c in enumerate(automat._modelchildren[row:], row):
							self.automat2model(c)
							c._modelrow = idx
						self.endInsertRows()
					self.dataChanged.emit(index.siblingAtColumn(0), index.siblingAtColumn(3))
					return True
				elif value is not None:
					#value is array of 2 strings
					automat = next(child for child in automat._modelchildren if child.name() == value[0])
					index = self.automat2index(automat)
					return self.setData(index, value[1], Qt.CreateChildRole)
			assert not automat.isProperty()
			if automat.upperMultiplicity() > 1:
				child = getattr(automat.parent().editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), automat.name()).append().model()
				parent = index.siblingAtColumn(0)
				row = automat.index(child)
				self.beginInsertRows(parent, row, row)
				automat._modelchildren.insert(row, child)
				for idx, c in enumerate(automat._modelchildren[row:], row):
					self.automat2model(c)
					c._modelrow = idx
				self.endInsertRows()
			else:
				assert not automat
				child = getattr(automat.parent().editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), automat.name())
				if isinstance(child, complexbase.ModelList):
					child = child[0].model()
				else:
					child = child.model()
				children = list(child.aggregations())
				self.beginInsertRows(index.siblingAtColumn(0), 0, len(children) - 1)
				automat._modelchildren.extend(children)
				for idx, c in enumerate(children):
					self.automat2model(c)
					c._modelrow = idx
				self.endInsertRows()
			self.dataChanged.emit(index.siblingAtColumn(0), index.siblingAtColumn(3))
			return True
		if role == Qt.DeleteRole:
			self._delete(value)
			return False
		return False
	def insertRows(self, row:int, count:int, parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]=...)->bool:
		QAbstractItemModel.insertRows(self, row, count, parent=parent)
	def moveRows(self, sourceParent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], sourceRow:int, count:int, destinationParent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], destinationChild:int)->bool:
		QAbstractItemModel.moveRows(self, sourceParent, sourceRow, count, destinationParent, destinationChild)
	def removeRows(self, row:int, count:int, parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]=...)->bool:
		automat=parent.internalPointer()
		if automat._aggr and automat._array:
			self.beginRemoveRows(parent, row, row+count-1)
			for child in automat._modelchildren[row:row+count]:
				automat._modelchildren.remove(child)
				child.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).delete()
			for row,child in enumerate(automat._modelchildren[row+count:],row):
				child._modelrow=row
			self.endRemoveRows()
			return True
		# rows not removable,
		return False			
	def _addedModel(self,automat):
		parent = next(binding for binding in automat.parent()._modelchildren if binding == automat.binding())
		assert parent.upperMultiplicity() > 1, 'GoMAT error, this function shall only be used for containers with mult > 1'
		Model.automat2model(parent)
		parentidx=self.createIndex(parent._modelrow, 0, parent)
		row = list(parent).index(automat)
		self.beginInsertRows(parentidx, row, row)
		parent._modelchildren.insert(row, automat)
		for idx, child in enumerate(parent._modelchildren[row:], row):
			Model.automat2model(child)
			child._modelrow = idx
		self.endInsertRows()
		self.dataChanged.emit(parentidx, parentidx.siblingAtColumn(3))
	def hasChildren(self, parent):
		automat=parent.internalPointer()
		if automat is None:
			return True
		if isinstance(automat, complexbase.ContainerBase):
			automat = self.automat2model(automat)
			return len(automat._modelchildren) > 0
		else:
			return not automat.isProperty() or automat.upperMultiplicity() > 1
			# return not automat.isProperty()
	def _delete(self,path):
		if path != '/':
			model=eval('autosar'+'.'.join(path.split('/')))
		else:
			model=autosar
		selected_file = mainWindow.fileselector.currentText()
		if isinstance(model,complexbase.ContainerBase):
			if selected_file:
				files = model.file()
				if selected_file not in files:
					# model not in file, return
					return
				if len(files) > 1:
					for child in model.children():
						if '_modelchildren' in child.__dict__ and selected_file in child.file():
							self._delete(str(child))
					model.editor(selected_file).delete()
					index=self.automat2index(model).parent()
					self.dataChanged.emit(index, index.siblingAtColumn(3))
					return
			if model.binding().upperMultiplicity() > 1:
				index=self.automat2index(model).parent()
				row=model._modelrow
				parent=model._modelparent
				self.beginRemoveRows(index, row, row)
				parent._modelchildren.pop(row)
				model.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).delete()
				for idx, sibling in enumerate(parent._modelchildren[row:],row):
					self.automat2model(sibling)
					sibling._modelrow=idx
				self.endRemoveRows()
			else:
				index=self.automat2index(model)
				model.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).delete()
				if len(index.internalPointer()._modelchildren):
					c = index.internalPointer()
					self.beginRemoveRows(index, 0, len(c._modelchildren))
					c._modelchildren = []
					self.endRemoveRows()
			self.dataChanged.emit(index, index.siblingAtColumn(3))
		else:
			for child in model:
				self._delete(child.__str__())


class ProxyModel(QAbstractProxyModel):
	def setSourceModel(self, sourceModel:PySide6.QtCore.QAbstractItemModel)->None:
		self.beginResetModel()
		super().setSourceModel(sourceModel)
		#self._source2proxy={autosar:self._root}
		sourceModel.rowsMoved.connect(self._modelRowsMoved)
		sourceModel.rowsInserted.connect(self._rowsInserted)
		sourceModel.rowsRemoved.connect(self._rowsRemoved)
		sourceModel.dataChanged.connect(self._dataChanged)
		sourceModel.layoutAboutToBeChanged.connect(lambda :self.layoutAboutToBeChanged.emit())
		sourceModel.layoutChanged.connect(lambda :self.layoutChanged.emit())
		self.endResetModel()

	def __init__(self, model):
		super().__init__(model)
		autosar._proxyrow = 0
		autosar._proxychildren = autosar._modelchildren[:]
		self.setSourceModel(model)
	def mapFromSource(self, sourceIndex:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->PySide6.QtCore.QModelIndex:
		automat=sourceIndex.internalPointer()
		if automat is None:
			return QModelIndex()
		elif isinstance(automat, complexbase.Aggregation) and isinstance(automat.parent(), (autosar_r4p0.Group.EcucIndexableValue, autosar_r4p0.EcucModuleConfigurationValues)):
			return QModelIndex()
			return sourceIndex
		model=ProxyModel.automat2model(automat)
		if model is None:
			return QModelIndex()
		return self.createIndex(model._proxyrow, sourceIndex.column(), model)
	def mapToSource(self, proxyIndex:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->PySide6.QtCore.QModelIndex:
		automat=proxyIndex.internalPointer()
		if automat is None:
			return QModelIndex()
		elif isinstance(automat, complexbase.EcucAggregation):
			return QModelIndex()
			return proxyIndex
		model = Model.automat2model(automat)
		return self.sourceModel().createIndex(model._modelrow, proxyIndex.column(), model)

	def automat2index(self, automat):
		automat = ProxyModel.automat2model(automat)
		if automat is None:
			return QModelIndex()
		return self.createIndex(automat._proxyrow, 0, automat)
	@staticmethod
	def automat2model(automat):
		if '_proxychildren' in automat.__dict__:
			# this automat is the model
			return automat
		if isinstance(automat, complexbase.EcucAggregation):
			parent = automat.parent()
			parent = ProxyModel.automat2model(parent)
			automat._proxyrow = parent._proxychildren.index(automat)
			automat._proxyparent = parent
			if automat and automat.upperMultiplicity() == 1:
				automat[0]._proxy = automat
				automat._proxychildren = list(automat[0].ecucAggregations())
				Model.automat2model(automat[0])
				if isinstance(automat[0], autosar_r4p0.Group.Referrable):
					shortName = next(child for child in automat[0]._modelchildren if child.name() == 'shortName')
					automat._proxychildren.insert(0, shortName)
			else:
				automat._proxychildren = list(automat)
			for row,child in enumerate(automat._proxychildren):
				child._proxyrow=row
				child._proxyparent=automat
			return automat
		elif isinstance(automat, autosar_r4p0.Group.EcucIndexableValue):
			if '_proxy' in automat.__dict__:
				return automat._proxy
			if not automat.definition.ref():
				return None
			if '_proxyparent' in automat.__dict__:
				# parent already configured
				Model.automat2model(automat)
			else:
				Model.automat2model(automat)
				parent = ProxyModel.automat2model(automat.parent())
				if parent is None:
					return None
				binding = automat.ecucBinding()
				if binding.upperMultiplicity() == 1:
					# update parent to get the binding
					ret = next(child for child in parent._proxychildren if child == binding)
					automat._proxy = ret
					ProxyModel.automat2model(ret)
					return ret
				automat._proxyparent = next(child for child in parent._proxychildren if child == binding)
				automat._proxyrow = automat._proxyparent._proxychildren.index(automat)
			automat._proxychildren = list(automat.ecucAggregations())
			if isinstance(automat, autosar_r4p0.Group.Referrable):
				shortName = next(child for child in automat._modelchildren if child.name() == 'shortName')
				automat._proxychildren.insert(0, shortName)
			return automat
		automat = Model.automat2model(automat)
		parent = ProxyModel.automat2model(automat._modelparent)
		if isinstance(automat, complexbase.Aggregation) and isinstance(parent, (autosar_r4p0.Group.EcucIndexableValue,autosar_r4p0.EcucModuleConfigurationValues)) and automat.name() != 'shortName':
			# this is a container witin an BSW config, return None to ignore
			return None
		if parent is None:
			return None
		automat._proxyparent = parent
		automat._proxyrow = automat._modelrow
		if isinstance(automat, autosar_r4p0.Group.EcucModuleConfigurationValues):
			automat._proxychildren = list(automat.ecucAggregations())
			shortName = next(child for child in automat._modelchildren if child.name() == 'shortName')
			automat._proxychildren.insert(0, shortName)
		else:
			automat._proxychildren = automat._modelchildren[:]
		return automat	
	def parent(self,child):
		automat=child.internalPointer()
		if automat is autosar:
			return QModelIndex()
		try:
			return self.createIndex(automat._proxyparent._proxyrow, 0, automat._proxyparent)
		except:
			QModelIndex()
	def index(self, row, column , parent):
		if not parent.isValid():
			if row != 0:
				return QModelIndex()
			# assert row == 0
			return self.createIndex(row, column, autosar)
		automat = parent.internalPointer()
		if row >= len(automat._proxychildren):
			return QModelIndex()
		return self.createIndex(row, column, ProxyModel.automat2model(automat._proxychildren[row]))
	def rowCount(self,parent):
		if not parent.isValid():
			return 1
		automat=parent.internalPointer()
		return len(automat._proxychildren)
	
	def _renumberChildren(self, parentidx):
		parent = parentidx.internalPointer()
		for row, sibling in enumerate(parent):
			if sibling._proxyrow != row:
				oldRow = sibling._proxyrow
				# will always move row to lower number
				self.beginMoveRows(parentidx, oldRow, oldRow, parentidx, row)
				parent._proxychildren.remove(sibling)
				parent._proxychildren.insert(row, sibling)
				sibling._proxyrow = row
				for i in range(row + 1, oldRow + 1):
					#assert parent._proxychildren[i]._proxyrow == i + 1, f"invalid proxyrow  for {parent} expected {i + 1}, was {parent._proxychildren[i]._proxyrow}"
					parent._proxychildren[i]._proxyrow += 1
				self.endMoveRows()

	def _modelRowsMoved(self,index,start,end,destination,row):
		automat = index.internalPointer()
		automat = ProxyModel.automat2model(automat)
		if automat is None:
			return
		mappedIdx=self.mapFromSource(index)
		if not mappedIdx.isValid():
			return
		assert index==destination, 'only support move within same parent, use delete and insert instead'
		self.beginMoveRows(mappedIdx, start, end, mappedIdx, row)
		for child in automat._modelchildren:
			ProxyModel.automat2model(child)
			child._proxyrow = child._modelrow
		self.endMoveRows()

	def _rowsInserted(self, parent, first, last):
		automat = parent.internalPointer()
		if not isinstance(automat, complexbase.ContainerBase) and isinstance(automat.parent(), (autosar_r4p0.Group.EcucIndexableValue, autosar_r4p0.Group.EcucModuleConfigurationValues)):
			parent = automat.parent()
			if '_proxyparent' in parent.__dict__:
				pass
			elif '_proxy' in parent.__dict__:
				parent = parent._proxy
			else:
				return
			# parent is referrable, i.e. child is container or property
			for idx in range(first, last + 1):
				child = automat._modelchildren[idx]
				try:
					binding = child.ecucBinding()
					binding = next(b for b in parent._proxychildren if b == binding)
				except:
					# ignore new subcontainers to configuration that is not configuration
					continue
				if '_proxychildren' not in binding.__dict__:
					continue
				parentidx = self.createIndex(binding._proxyrow, 0, binding)
				if binding.upperMultiplicity() > 1:
					row = list(binding).index(child)
					self.beginInsertRows(parentidx, row, row)
					binding._proxychildren.insert(row, child)
					for idx, m in enumerate(binding._proxychildren[row:], row):
						ProxyModel.automat2model(m)
						m._proxyrow = idx
					self.endInsertRows()
					self.dataChanged.emit(parentidx, parentidx.siblingAtColumn(3), Qt.EditRole)
					continue
				elif isinstance(child, autosar_r4p0.Group.Referrable):
					# added a single instance
					aggregations = list(binding[0].ecucAggregations())
					aggregations.insert(0, Model.automat2model(child.shortName))
					if len(binding._proxychildren) == 0:
						self.beginInsertRows(parentidx, 0, len(aggregations) - 1)
						binding._proxychildren = aggregations
						for idx, m in enumerate(binding._proxychildren):
							ProxyModel.automat2model(m)
							m._proxyrow = idx
						self.endInsertRows()
					self.dataChanged.emit(parentidx, parentidx.siblingAtColumn(3), Qt.EditRole)					
				self.dataChanged.emit(parentidx.parent(), parentidx.parent().siblingAtColumn(3), Qt.EditRole)
			return
		if '_proxychildren' in automat.__dict__:
			parentidx = self.createIndex(automat._proxyrow, 0, automat)
			self.beginInsertRows(parentidx, first, last)
			for idx in range(first, last + 1):
				child = automat._modelchildren[idx]
				automat._proxychildren.insert(idx, child)
			for child in automat._proxychildren[first:]:
				ProxyModel.automat2model(child)
				child._proxyrow = child._modelrow
			self.endInsertRows()

	def _rowsRemoved(self, parent, first, last):
		automat=parent.internalPointer()
		if isinstance(automat, complexbase.ContainerBase) or not isinstance(automat.parent(), (autosar_r4p0.Group.EcucIndexableValue, autosar_r4p0.Group.EcucModuleConfigurationValues)):
			if '_proxychildren' in automat.__dict__:
				self.beginRemoveRows(self.createIndex(automat._proxyrow, 0, automat), first, last)
				del automat._proxychildren[first:last+1]
				for idx, child in enumerate(automat._proxychildren[first:], first):
					child._proxyrow=idx
				self.endRemoveRows()
				self._dataChanged(parent, parent.siblingAtColumn(3), Qt.EditRole)
			return
		automat = automat.parent()
		if '_proxy' in automat.__dict__:
			automat = automat._proxy
		if '_proxychildren' not in automat.__dict__:
			return
		for child in automat._proxychildren:
			if '_proxychildren' in child.__dict__:
				l = len(child)
				if l < len(child._proxychildren):
					index = self.createIndex(child._proxyrow, 0, child)
					if child.upperMultiplicity() > 1:
						for idx, c in enumerate(child):
							if idx != c._proxyrow:
								rowsRemoved = c._proxyrow - idx
								self.beginRemoveRows(index, idx, idx + rowsRemoved - 1)
								del child._proxychildren[idx:idx + rowsRemoved]
								for i, x in enumerate(child._proxychildren[idx:], idx):
									x._proxyrow -= rowsRemoved
								self.endRemoveRows()
						if l < len(child._proxychildren):
							self.beginRemoveRows(index, l, len(child._proxychildren) - 1)
							del child._proxychildren[l:]
							self.endRemoveRows()
					elif not child:
						if child._proxychildren:
							self.beginRemoveRows(index, 0, len(child._proxychildren) - 1)
							child._proxychildren = []
							self.endRemoveRows()
					self.dataChanged.emit(index, index.siblingAtColumn(3), Qt.EditRole)
	@staticmethod
	def _clearProxy(parent):
		if '_proxyparent' in parent.__dict__:
			del parent._proxyparent
			del parent._proxyrow
			if '_proxychildren' in parent.__dict__:
				for child in parent._proxychildren:
					if isinstance(child, complexbase.EcucAggregation) and child and child.upperMultiplicity() == 1:
						try:
							del child[0]._proxy
						except:
							raise
					ProxyModel._clearProxy(child)
				del parent._proxychildren

	def _dataChanged(self, topLeft, bottomRight, roles):
		assert topLeft.row() == bottomRight.row()
		automat=topLeft.internalPointer()
		if automat is None:
			return
		parent=automat.parent()
		if isinstance(parent, (autosar_r4p0.Group.EcucIndexableValue, autosar_r4p0.Group.EcucModuleConfigurationValues)) and isinstance(automat, complexbase.Aggregation):
			if automat.name() == 'definition':
				# check if wrong
				if '_proxy' in parent.__dict__:
					if parent._proxy[0] == parent:
						# nothing has changed return
						return
					proxy = parent._proxy
					parentidx = self.createIndex(proxy._proxyrow, 0, proxy)
					self.beginRemoveRows(parentidx, 0, len(proxy._proxychildren))
					del parent._proxy
					proxy._proxychildren.clear()
					self.endRemoveRows()
					# self._dataChanged.emit(parentidx, parentidx.siblingAtColumn(3), roles)
				elif '_proxyparent' in parent.__dict__:
					parentidx = self.createIndex(parent._proxyparent._proxyrow, 0, parent._proxyparent)
					row = parent._proxyparent._proxychildren.index(parent)
					self.beginRemoveRows(parentidx, row, row)
					children = parent._proxyparent._proxychildren
					ProxyModel._clearProxy(children.pop(row))
					for idx, child in enumerate(children[row:], row):
						child._proxyrow = idx
					self.endRemoveRows()
				else:
					return
				if isinstance(parent, autosar_r4p0.Group.EcucModuleConfigurationValues):
					binding = parent.binding()
				else:
					binding = parent.ecucBinding()
					if binding is None:
						# this model is invalid, ignore it
						return
				binding = next((child for child in ProxyModel.automat2model(parent.parent())._proxychildren if child == binding), None)
				if binding is None:
					# definition is invalid, return
					return
				if '_proxychildren' not in binding.__dict__:
					return
				parentidx = self.createIndex(binding._proxyrow, 0, binding)
				# parent = ProxyModel.automat2model(parent.parent())
				if binding.upperMultiplicity() > 1:
					row = binding.index(parent)
					self.beginInsertRows(parentidx, row, row)
					parent._proxyparent = binding
					parent._proxyrow = row
					binding._proxychildren.insert(row, parent)
					for idx, child in enumerate(binding._proxychildren[row:], row):
						ProxyModel.automat2model(child)
						child._proxyrow = idx
					self.endInsertRows()
				else:
					parent._proxy = binding
				self.dataChanged.emit(parentidx, parentidx.siblingAtColumn(3), roles)
				return
			elif automat.name() == 'index':
				# rows might have been moved
				if '_proxyparent' not in parent.__dict__:
					# model not loaded, ignore change
					return
				parentidx=self.automat2index(parent._proxyparent)
				for idx,child in enumerate(parent._proxyparent):
					if child._proxyrow != idx:
						self.beginMoveRows(parentidx, child._proxyrow, child._proxyrow, parentidx, idx)
						parent._proxyparent._proxychildren.remove(child)
						parent._proxyparent._proxychildren.insert(idx, child)
						for i in range(idx, child._proxyrow + 1):
							parent._proxyparent._proxychildren[i]._proxyrow = i
						self.endMoveRows()
				return
			elif '_proxychildren' in parent.__dict__:
				parentmodel = parent
			elif '_proxy' in parent.__dict__:
				parentmodel = parent._proxy
			else:
				return
			parentidx = self.createIndex(parentmodel._proxyrow, 0, parentmodel)
			if isinstance(parent, autosar_r4p0.Group.Referrable):
				if isinstance(automat, autosar_r4p0.Group.Referrable):
					# ignore event
					return
				if automat.name() == 'shortName':
					automatmodel = ProxyModel.automat2model(automat)
					idx = self.createIndex(automatmodel._proxyrow, 1, automatmodel)
					self.dataChanged.emit(idx, idx, roles)
					self.dataChanged.emit(parentidx,parentidx,roles)
					parent = automat.parent()
					if isinstance(parent, autosar_r4p0.Group.EcucIndexableValue) and parent.ecucBinding().upperMultiplicity() > 1:
						self._renumberChildren(parentidx.parent())
				return
			elif automat.name() == 'value':
				self.dataChanged.emit(parentidx, parentidx.siblingAtColumn(3), roles)
				return
			elif automat.name() == 'index':
				self._renumberChildren(parentidx.parent())
				return
			elif automat.name() == 'isAutoValue':
				self.dataChanged.emit(parentidx,parentidx.siblingAtColumn(3),roles)
				return
		else:
			self.dataChanged.emit(self.mapFromSource(topLeft),self.mapFromSource(bottomRight),roles)
			
	def moveRows(self, sourceParent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], sourceRow:int, count:int, destinationParent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], destinationChild:int)->bool:
		QAbstractItemModel.moveRows(self, sourceParent, sourceRow, count, destinationParent, destinationChild)
	def canDropMimeData(self, data:PySide6.QtCore.QMimeData, action:PySide6.QtCore.Qt.DropAction, row:int, column:int, parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->bool:
		d=data.data("AutoMAT/row/BSW")
		if not d:
			return super().canDropMimeData(data, action, row, column, parent)
		rows=d.split('\n')
		if len(rows) > 1:
			return False
		model=eval(d)
		dest=parent.internalPointer()
		if not dest:
			return False
		if not isinstance(dest, complexbase.EcucAggregation):
			return False
		if dest.parent() is not model.parent():
			return False
		if model._proxyrow <= row <=model._proxyrow+1:
			return False
		return bool(model.definition.ref().requiresIndex.val())
	def dropMimeData(self, data, action, row, column, parent):
		d=data.data("AutoMAT/row/BSW")
		if not d:
			return super().canDropMimeData(data, action, row, column, parent)
		rows=d.split('\n')
		model=eval(d)
		children=list(model.binding())
		children.remove(model)
		children.insert(row,model)
		sourceModel = self.sourceModel()
		for idx,child in enumerate(children):
			if child.index.val() != idx:
				aggr=next(a for a in child._modelchildren if a.name()=='index')
				sourceModel.setData(sourceModel.automat2index(aggr),idx,Qt.EditRole)
		return False
	def mimeData(self, indexes)->PySide6.QtCore.QMimeData:
		automat=indexes[0].internalPointer()
		if isinstance(automat,complexbase.EcucAggregation):
			data='\n'.join((idx.internalPointer().__repr__() for idx in indexes if idx.column() == 0))
		elif isinstance(automat, (autosar_r4p0.Group.EcucIndexableValue,autosar_r4p0.Group.EcucModuleConfigurationValues)):
			data='\n'.join((idx.internalPointer().__repr__() for idx in indexes if idx.column() == 0))
		else:
			return super().mimeData(indexes)
		mimeData = QMimeData()
		mimeData.setData("AutoMAT/row/BSW", QByteArray(data));
		return mimeData;
	def mimeTypes(self)->List[str]:
		return ('AutoMAT/row/AUTOSAR', 'AutoMAT/row/BSW')
	def supportedDropActions(self):
		return Qt.CopyAction | Qt.MoveAction
	def columnCount(self,parent):
		return 4
	def flags(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])->PySide6.QtCore.Qt.ItemFlags:
		automat = index.internalPointer()
		if isinstance(automat, complexbase.EcucAggregation):
			try:
				if isinstance(automat.definition(), autosar_r4p0.Group.EcucParameterDef) and automat.definition().withAuto.val():
					return Qt.NoItemFlags
			except:
				raise
		elif isinstance(automat, autosar_r4p0.Group.EcucIndexableValue):
			binding = automat.ecucBinding()
			if isinstance(binding.definition(), autosar_r4p0.Group.EcucParameterDef) and binding.definition().withAuto.val():
				return Qt.NoItemFlags
		else:
			return super().flags(index)
		ret=Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled|Qt.ItemIsDropEnabled
		if index.column() == 1:
			container = isinstance(automat, autosar_r4p0.Group.EcucIndexableValue)
			if container and automat.ecucBinding().isProperty() or not container and (automat.isProperty() or automat.desttype().__class__ is list):
				ret |= Qt.ItemIsEditable
		return ret
	def status(self, automat):
		try:
			automat.validate()
			if isinstance(automat, complexbase.Aggregation) and automat and automat.upperMultiplicity() == 1:
				automat[0].validate()
			return ''
		except AssertionError as e:
			return str(e)
		except Exception as e:
			vprint(f'Unexpected exception in rule file {e.__traceback__.tb_next.tb_frame.f_code.co_filename}:{e.__traceback__.tb_next.tb_frame.f_lineno} for {automat}: {e}')
	def data(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], role:int=...)->Any:
		automat=index.internalPointer()
		if role == Qt.AutoMATRole:
			return automat
		if role == Qt.DecorationRole:
			if index.column()==0:
				if self.status(automat):
					return ErrorIcon					
				if automat.parent().editor().readonly():
					return PadLockIcon
			return None
		if role in (Qt.WhatsThisRole,Qt.ToolTipRole):
			ret = ''
			if isinstance(automat, complexbase.EcucAggregation):
				if index.column() <= 1:
					# property/value column
					ret=automat.__doc__
				elif index.column()==2:
					# type column
					ret=str(automat.definition())
				status=self.status(automat)
			elif isinstance(automat, (autosar_r4p0.Group.EcucIndexableValue,autosar_r4p0.Group.EcucModuleConfigurationValues)):
				if index.column() < 2:
					# property/value column
					ret=automat.definition.ref().desc.l2[0].val()
				elif index.column()==2:
					# type column
					ret=str(automat.definition.ref())
				status=self.status(automat)
			else:
				if index.column() <= 1:
					# property/value column
					ret=automat.binding().__doc__
				status=self.status(automat)
			if status:
				if ret:
					ret+='\n'
				ret+='Error: '+status
			return ret
			
		if isinstance(automat,complexbase.EcucAggregation):
			if automat and automat.upperMultiplicity() == 1:
				automat = automat[0]
			else:
				try:
					getattr(automat.parent(), automat.name())
				except Exception as e:
					pass
				if automat.upperMultiplicity() == 1 and (defaultval := getattr(automat.parent(), automat.name())):
					# this is the default value of a property
					if role == Qt.FontRole:
						if index.column() == 1:
							font = QFont()
							font.setItalic(True)
							font.setWeight(QFont.Weight.Thin)
							return font
					elif role == Qt.DisplayRole:
						if index.column() == 1:
							if issubclass(automat.valuetype(), int) and action_showHex.isChecked():
								val=hex(defaultval.val())
							else:
								val=str(defaultval.val())
							return '<DEFAULT> ' + val
					elif role == Qt.OkToHideRole:
						return False
				if role == Qt.DisplayRole:
					if index.column() == 0:
						name=automat.name()
						if automat.upperMultiplicity()>1:
							return name + ('s{' if name[-1] != 's' else 'es{') + str(len(automat)) + '}'
						else:
							return name
					if index.column() == 1:
						if automat.isProperty():
							return '-'
						return None
					if index.column()==2:
						return str(automat.definition().shortName.val())
					if index.column()==3:
						files = automat.parent().file_line()
						return str(files if len(files) > 1 else files[0])
				elif role == Qt.ForegroundRole:
					if not automat:
						return app.palette().color(QPalette.Disabled, QPalette.Text)
					selectedfile = mainWindow.fileselector.currentText()
					if selectedfile:
						for child in automat:
							if selectedfile in child.file():
								return app.palette().color(QPalette.Highlight)
					return None
				elif role==Qt.OkToHideRole:
					return not automat and not self.status(automat)
				elif role == Qt.AddListRole:
					if automat.parent().editor().readonly() or automat.isProperty():
						return None
					desttype = automat.desttype()
					if automat.upperMultiplicity() > len(automat):
						if isinstance(desttype, list):
							return {t:None for t in desttype}
						return True
					return None
				return None
		if role == Qt.AddListRole and isinstance(automat, (autosar_r4p0.Group.EcucIndexableValue, autosar_r4p0.Group.EcucModuleConfigurationValues)):
			ret = {}
			for child in automat.ecucAggregations():
				if child.isProperty():
					continue
				if child.upperMultiplicity() <= len(child):
					continue
				desttype = child.desttype()
				if isinstance(desttype, list):
					if len(desttype) == 1:
						desttype = desttype[0]
					else:
						ret[child.name()] = list(desttype)
						continue
				ret[child.name()] = None
			return ret
		elif isinstance(automat, autosar_r4p0.Group.EcucIndexableValue):
			if role == Qt.DisplayRole:
				if index.column() == 0:
					binding = automat.ecucBinding()
					if binding.sortable():
						if isinstance(automat,autosar_r4p0.Group.Referrable):
							return f'[{automat._proxyrow}]: {automat.shortName.val()}'
						else:
							return f'[{automat._proxyrow}]'
					if isinstance(automat,autosar_r4p0.Group.Referrable) and binding.upperMultiplicity()>1:
						return automat.shortName.val()
					return binding.name()
				if index.column() == 1:
					if isinstance(automat, complexbase.ValueTypeBase):
						if issubclass(automat.ecucBinding().valuetype(), int):
							return hex(automat.val()) if action_showHex.isChecked() else str(automat.val())
						return str(automat.val())
					return None
				if index.column()==2:
					return automat.definition.ref().shortName.val()
				if index.column()==3:
					files = automat.file_line()
					return str(files if len(files) > 1 else files[0])
			elif role == Qt.ForegroundRole:
				selectedfile = mainWindow.fileselector.currentText()
				if selectedfile:
					if selectedfile in automat.file():
						return app.palette().color(QPalette.Highlight)
			return None
		return super().data(index, role)

	def setData(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], value:Any, role:int=...) -> bool:
		if role == Qt.DeleteRole:
			self._delete(value)
			return False
		automat=index.internalPointer()
		if isinstance(automat,complexbase.EcucAggregation):
			if automat and automat.upperMultiplicity() == 1:
				automat = automat[0]
			else:
				for child in automat.parent()._modelchildren:
					Model.automat2model(child)
				file = mainWindow.fileselector.currentText()
				if not file:
					if len(automat.parent().file()) == 1:
						file = automat.parent().file()[0]
					else:
						file_dialog = SelectFileDialog(automat.parent().file())
						file_dialog.exec_()
						file = file_dialog.get_selected_file()
				if role == Qt.EditRole:
					if automat.upperMultiplicity() == 1:
						setattr(automat.parent().editor(file=file), automat.name(), value)
						newModel=automat[0]
					else:
						newModel = getattr(automat.parent().editor(file=file), automat.name()).append(value).model()
					self.sourceModel()._addedModel(newModel)
					return True
				elif role == Qt.CreateChildRole:
					assert value is None
					if automat.upperMultiplicity() > 1:
						newModel = getattr(automat.parent().editor(file=file), automat.name()).append().model()
					else:
						assert len(automat) == 0
						newModel = getattr(automat.parent().editor(file=file), automat.name()).model()
					self.sourceModel()._addedModel(newModel)
					return True
				return False
		if role == Qt.CreateChildRole and isinstance(automat, (autosar_r4p0.Group.EcucIndexableValue,autosar_r4p0.Group.EcucModuleConfigurationValues)):
			child = getattr(automat.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None), value)
			if isinstance(child, complexbase.EcucList):
				child = child.append()
			child = child.model()
			self.sourceModel()._addedModel(child)
			return True
		elif isinstance(automat, autosar_r4p0.Group.EcucIndexableValue):
			if role == Qt.EditRole:
				automat.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).set(value)
				sourceModel = self.sourceModel()
				node=Model.automat2model(automat.value)
				sourceIdx = sourceModel.createIndex(node._modelrow, 0, node)
				sourceModel.dataChanged.emit(sourceIdx, sourceIdx.siblingAtColumn(3))
				return True
		else:
			return super().setData(index, value, role)
		return False
	def hasChildren(self, parent):
		automat = parent.internalPointer()
		if automat is None:
			return True
		if isinstance(automat, complexbase.ContainerBase):
			automat = ProxyModel.automat2model(automat)
			if automat is None:
				return False
			return len(automat._proxychildren) > 0
		else:
			return not automat.isProperty() or automat.upperMultiplicity() > 1
	def buddy(self, idx):
		return idx

	def _delete(self,path):
		model=eval('autosar'+'.'.join(path.split('/')))
		if isinstance(model, complexbase.ContainerBase):
			if model.binding().name() == 'shortName':
				# we do not allow delettion of shortNames
				print("Not allowed to delete a shortName")
				return
			self.sourceModel()._delete(path)
		elif isinstance(model, complexbase.DefaultValueType):
			return
		else:
			for child in model:
				self.sourceModel()._delete(child.__str__())


class ModelView(QTreeView):
	_refModel = None

	class Delegate(QStyledItemDelegate):

		def activatedCalled(self, index, editor):
			# self.parent().setCurrentIndex(index)
			self.commitData.emit(editor)
			self.closeEditor.emit(editor)

		def createEditor(self, parent, option, index):
			automat = index.model().data(index, Qt.AutoMATRole)
			if isinstance(automat, autosar_r4p0.Group.EcucIndexableValue):
				binding = automat.ecucBinding()
			elif isinstance(automat, complexbase.ContainerBase):
				binding = automat.binding()
			else:
				binding = automat
				if automat and binding.upperMultiplicity() == 1:
					automat = automat[0]
			if not binding.isProperty():
				ret = QComboBox(parent)
				if isinstance(automat, complexbase.EcucAggregation):
					vals = automat.desttype()
				else:
					vals = list(t.__name__ for t in automat.desttype())
				ret.addItems(sorted((str(val) for val in vals), key=str.lower))
				ret.activated.connect(lambda i: self.activatedCalled(i, ret))
				ret.role = Qt.CreateChildRole
			elif hasattr(binding, 'values'):
				ret = QComboBox(parent)
				vals = binding.values()
				if len(vals) == 0:
					return None
				ret.addItems(sorted((str(val) for val in vals), key=str.lower))
				ret.activated.connect(lambda i: self.activatedCalled(i, ret))
				ret.role = Qt.EditRole
			else:
				ret = QLineEdit(parent)
				ret.role = Qt.EditRole
				if hasattr(binding, 'regex'):
					rx = QRegularExpression(binding.regex())
					validator = QRegularExpressionValidator(rx, ret)
					ret.setValidator(validator)
					ret.setToolTip(binding.regex())
				elif hasattr(binding, 'valuetype'):
					valuetype = binding.valuetype()
					if valuetype == float:
						lo = QLocale(QLocale.C)
						lo.setNumberOptions(QLocale.RejectGroupSeparator)
						validator = QDoubleValidator(ret)  # todo: min max
						validator.setLocale(lo)
						try:
							validator.setBottom(binding.definition().min.val())
						except: pass
						try:
							validator.setTop(binding.definition().max.val())
						except: pass
						ret.setValidator(validator)
					elif valuetype == int:
						class IntValidator(QRegularExpressionValidator):
							_binding = binding
							def __init__(self, parent):
								super().__init__(QRegularExpression('[-+]?(0[xX][0-9a-fA-F]+|[0-9]+)'), parent)
							def validate(self, input, pos):
								ret = super().validate(input, pos)
								if ret[0] != QValidator.Acceptable or not isinstance(self._binding, complexbase.EcucAggregation):
									return ret
								val = int(input, 0)
								if self._binding.definition().min:
									minval = self._binding.definition().min.val()
									if val < minval:
										if minval <= 0:
											return QValidator.Invalid
										return QValidator.Intermediate
								if self._binding.definition().max:
									maxval = self._binding.definition().max.val()
									if val > maxval:
										if maxval >= 0:
											return QValidator.Invalid
										return QValidator.Intermediate
								return ret
						# validator = QRegularExpressionValidator(QRegularExpression('[-+]?(0[xX][0-9a-fA-F]+|[0-9]+)'), ret)  # todo: min max
						validator = IntValidator(ret)
						ret.setValidator(validator)
					elif issubclass(valuetype,simplebase.SimpleArrayElement):
						class ValArrayValidator(QValidator):
							def validate(self, input, pos):
								try:
									valuetype(input)
								except:
									return QValidator.Intermediate
								return QValidator.Acceptable
						validator = ValArrayValidator(ret)
						ret.setValidator(validator)
			ret.binding = binding
			ret.automat = automat
			return ret

		def setEditorData(self, editor, index):
			val = None
			if not isinstance(editor.automat, complexbase.ContainerBase):
				# non existent
				if hasattr(editor.automat, 'defaultValue') and editor.automat.defaultValue() != ModelNone:
					if issubclass(editor.binding.valuetype(), int) and action_showHex.isChecked():
						val=hex(editor.automat.defaultValue())
					else:
						val = editor.automat.defaultValue()
			else:
				if issubclass(editor.binding.valuetype(), int) and action_showHex.isChecked():
					val=hex(editor.automat.val())
				else:
					val = editor.automat.val()
			if val != None:
				if hasattr(editor, 'setCurrentText'):
					editor.setCurrentText(str(val))
				else:
					editor.setText(str(val))
			if isinstance(editor, QComboBox):
				editor.showPopup()

		def setModelData(self, editor, model, index):
			if hasattr(editor, 'currentText'):
				val = editor.currentText()
			else:
				val = editor.text()
				if hasattr(editor.binding, 'valuetype'):
					valuetype = editor.binding.valuetype()
					if valuetype is int:
						val = int(val, 0)
					else:
						val = valuetype(val)
			model.setData(index, val, editor.role)
			self.parent().setExpanded(index, True)

		def updateEditorGeometry(self, editor, option, index):
			editor.setGeometry(option.rect)

	class SelectionModel(QItemSelectionModel):
		def select(self, indexes, command):
			if 0 == (QItemSelectionModel.Select & command):
				#unselect
				return QItemSelectionModel.select(self, indexes, command)
			if not isinstance(indexes, QItemSelection):
				idxs = (indexes,)
			else:
				idxs = indexes.indexes()
			rows = self.selectedRows()
			if rows and 0 == (command & QItemSelectionModel.Clear):
				try:
					refnode = self.model().data(rows[0], Qt.AutoMATRole)
					command = QItemSelectionModel.Select | QItemSelectionModel.Rows
				except:
					return
			else:
				try:
					refnode = self.model().data(idxs[0], Qt.AutoMATRole)
					command = QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows
				except:
					QItemSelectionModel.select(self, indexes, command)
					return
			for idx in idxs:
				if idx in rows:
					continue
				automat = self.model().data(idx, Qt.AutoMATRole)
				if not automat:
					continue
				equal=False
				if isinstance(automat,complexbase.ContainerBase):
					if isinstance(refnode,complexbase.ContainerBase):
						if automat.binding().desttype() == refnode.binding().desttype():
							if isinstance(automat,autosar_r4p0.Group.EcucIndexableValue) and isinstance(refnode,autosar_r4p0.Group.EcucIndexableValue):
								if automat.ecucBinding().desttype() == refnode.ecucBinding().desttype():
									equal=True
							else:
								equal=True
				elif not isinstance(refnode, complexbase.ContainerBase) and automat.desttype() == refnode.desttype():
					equal=True						
				if equal:
					QItemSelectionModel.select(self, QItemSelection(idx, idx), command)
					command = QItemSelectionModel.Select | QItemSelectionModel.Rows

	def _restoreSettings(self):
		self.setColumnWidth(0, self.settings.value('column0 width', 250, int))
		self.setColumnWidth(1, self.settings.value('column1 width', 250, int))
		self.setColumnWidth(2, self.settings.value('column2 width', 250, int))
		selections = self.settings.value('selection', '', str).split(',')
		return
		for selection in selections:
			selection = autosar if selection == '/' else eval('autosar' + '.'.join(selection.split('/')))
			if isinstance(selection, ModelList):
				selection = selection.binding()
			elif not selection:
				continue
			idx = model.automat2index(selection)
			self.selectionModel().select(QItemSelection(idx, idx.siblingAtColumn(3)), QItemSelectionModel.SelectCurrent)
			# self.selectionChanged(QItemSelection(idx, idx.siblingAtColumn(3)), QItemSelection())
			self.scrollTo(idx)

	def _saveSettings(self):
		if self.columnWidth(0):
			self.settings.setValue('column0 width', self.columnWidth(0))
		if self.columnWidth(1):
			self.settings.setValue('column1 width', self.columnWidth(1))
		if self.columnWidth(2):
			self.settings.setValue('column2 width', self.columnWidth(2))
		self.settings.setValue('selection', ','.join(str(idx.model().data(idx, Qt.AutoMATRole)) for idx in self.selectedIndexes() if idx.isValid() and idx.column() == 0))
		self.settings.sync()
	def __init__(self, object, model, settings):
		QTreeView.__init__(self, object)
		self.setModel(model)
		self.expandToDepth(1)
		self.settings = settings
		self.setItemDelegate(self.Delegate(self))
		self.setEditTriggers(QTreeView.EditTrigger.DoubleClicked | QTreeView.EditTrigger.SelectedClicked | QTreeView.EditTrigger.AnyKeyPressed)
		self.expanded.connect(self.onExpanded)
		#self._selectionModel=self.SelectionModel(model)
		self.setSelectionModel(self.SelectionModel(model))
		self.setSelectionMode(QAbstractItemView.ExtendedSelection)
		self.setSelectionBehavior(QAbstractItemView.SelectRows)
		# drag drop
		self.setDragEnabled(True);
		self.setAcceptDrops(True);
		self.setDropIndicatorShown(True);
		self._restoreSettings()
	def onExpanded(self,index):
		d = self.model().data(index, Qt.AutoMATRole)
		if isinstance(d,autosar_r4p0.Group.Referrable):
			for i in range(0, self.model().rowCount(index)):
				idx = self.model().index(i, 0, index)
				self.setExpanded(idx, True)

	def closeEvent(self, *args, **kwargs):
		self._saveSettings()
		return QTreeView.closeEvent(self, *args, **kwargs)
	def mouseDoubleClickEvent(self, event):
		idx = self.indexAt(event.pos())
		automat = self.model().data(idx, Qt.AutoMATRole)
		if idx.column() == 1 and not isinstance(automat, complexbase.ContainerBase):
			if automat.isProperty():
				if issubclass(automat.valuetype(), autosar_r4p0.SimpleTypes.Ref):
					print('jump to referred container')
			elif not (automat and automat.upperMultiplicity() == 1) and (not isinstance(automat.desttype(), list) or len(automat.desttype()) == 1):
				self.model().setData(idx, None, Qt.CreateChildRole)
				self.setExpanded(idx, True)
		QTreeView.mouseDoubleClickEvent(self, event)

	def contextMenuEvent(self, event):
		idx = self.indexAt(event.pos())
		if idx.column()==0:
			connections = []
			def selector(parent, first, last):
				newindex=self.model().index(first,0, parent)
				self.setExpanded(parent, True)
				self.setExpanded(newindex, True)
				self.selectionModel().clear()
				if self.model().rowCount(newindex) == 1:
					# the newly created container has a child, select that one instead
					newindex = self.model().index(0,0, newindex)
					self.setExpanded(newindex, True)
				self.selectionModel().select(newindex, QItemSelectionModel.SelectCurrent)
				self.setCurrentIndex(newindex)
				self.scrollTo(newindex)
			selectedrows = self.selectionModel().selectedRows()
			menu = QMenu(self)
			if idx.siblingAtColumn(0) not in selectedrows:
				self.clearSelection()
				selectedrows=[]
			if len(selectedrows) <= 1:
				addlist = self.model().data(idx, Qt.AddListRole)
				if addlist:
					connections.append(self.model().rowsInserted.connect(selector))
					if addlist is True:
						connections.append(menu.addAction('add').triggered.connect(lambda:self.model().setData(idx, None, Qt.CreateChildRole)))
					else:
						addmenu = menu.addMenu('add')
						groupedAddList = len(addlist) > 30
						if groupedAddList:
							currentGroup = ''
							addMenuOrg = addmenu
						for menuitem, subs in sorted(addlist.items(), key=lambda e:e[0].upper()):
							if groupedAddList and menuitem[0].upper() != currentGroup:
								currentGroup = menuitem[0].upper()
								addmenu = addMenuOrg.addMenu(currentGroup)
							if subs:
								cmenu = addmenu.addMenu(menuitem)
								groupedSubList = len(subs) > 30
								if groupedSubList:
									currentSubGroup = ''
									cmenuOrg = cmenu
								for sub in subs:
									if groupedSubList and sub[0].upper() != currentSubGroup:
										currentSubGroup = sub[0].upper()
										cmenu = cmenuOrg.addMenu(currentSubGroup)
									connections.append(cmenu.addAction(sub).triggered.connect(lambda *, val=(menuitem, sub),:self.model().setData(idx, val, Qt.CreateChildRole)))
							else:
								connections.append(addmenu.addAction(menuitem).triggered.connect(lambda *, menuitem=menuitem:self.model().setData(idx, menuitem, Qt.CreateChildRole)))
			if selectedrows:
				selected_file = mainWindow.fileselector.currentText()
				if selected_file:
					for row in selectedrows:
						automat = self.model().data(idx, Qt.AutoMATRole)
						if isinstance(automat, complexbase.ContainerBase):
							if selected_file not in automat.file():
								break
						else:
							if selected_file not in automat.parent().file():
								break
					else:
						menu.addAction('delete').triggered.connect(lambda: self._delete(selectedrows))
				else:
					menu.addAction('delete').triggered.connect(lambda: self._delete(selectedrows))
			menu.exec_(event.globalPos())
			for con in connections:
				QObject.disconnect(con)

	def _delete(self,indexes):
		#if self._refModel:
		#	self._refModel.setRoot(None, QModelIndex())
		paths = [self.model().data(idx, Qt.AutoMATRole).__str__() for idx in indexes]
		self.setCurrentIndex(QModelIndex())
		for path in paths:
			self.model().setData(QModelIndex(), path, Qt.DeleteRole)
		return
				
	def keyPressEvent(self, event:PySide6.QtGui.QKeyEvent)->None:
		if event.key()==Qt.Key_Delete:
			self._delete(self.selectionModel().selectedRows())
		else:
			super().keyPressEvent(event)

	def currentChanged(self, current, previous):
		if self._refModel:
			if current != previous:
				if self.model().data(current, Qt.AutoMATRole):
					self._refModel.setRoot(self.model().sourceModel(), self.model().mapToSource(current))
				else:
					self._refModel.setRoot(None, QModelIndex())
		return QTreeView.currentChanged(self, current, previous)

	def focusInEvent(self, event):
		if event.gotFocus() and self._refModel:
			idx = self.currentIndex()
			if idx:
				self._refModel.setRoot(self.model().sourceModel(), self.model().mapToSource(idx))
			else:
				self._refModel.setRoot(None, QModelIndex())
	
class DockWidget(QDockWidget):
	def __init__(self, name, widget):
		super().__init__(name)
		self.setFeatures(QDockWidget.DockWidgetFeature.DockWidgetMovable)
		self.setObjectName(name)
		# make it possible to set widget title and change dockwidget name
		widget.windowTitleChanged.connect(lambda e:self.setWindowTitle(e))
		self.widget=widget
		self.setWidget(widget)
		self.setAllowedAreas(Qt.AllDockWidgetAreas)
		self._emptyTitleBar = QWidget()
		#self.topLevelChanged.connect(lambda e:print(e))

	def closeEvent(self, *args, **kwargs):
		self.widget.close()
		return QDockWidget.closeEvent(self, *args, **kwargs)
		

class MainWindow(QMainWindow):
	def create_new_file(self):
		file_dialog = QFileDialog()
		settings=QSettings()
		settings.beginGroup('CreateArxmlDialog')
		defaultPath = settings.value('defaultPath','')
		if defaultPath and os.path.isdir(defaultPath):
			file_dialog.setDirectory(defaultPath)
		selected_file = file_dialog.getSaveFileName(None, None, None, '*.arxml')
		if selected_file[0]:
			self.fileselector.addItem(selected_file[0], QComboBox.InsertPolicy.InsertAlphabetically)
			self.fileselector.setCurrentText(selected_file[0])
			settings.setValue('defaultPath', os.path.dirname(selected_file[0]))
			settings.sync()

	def create_new_bsw(self):
		class BswDialog(QDialog):
			def __init__(self):
				super().__init__()
				self.settings=QSettings()
				self.settings.beginGroup('BswDialog')

				self.setWindowTitle('Create new BSW module')
				# Create a QVBoxLayout instance
				layout = QVBoxLayout()
				
				# Create a QLabel as the title for the combobox
				self.locationLabel = QLabel('Choose namespace for the new BSW module')
				layout.addWidget(self.locationLabel)
				
				# Create a QComboBox and add some items
				self.location = QComboBox()
				locations = sorted(p.__str__() for p in autosar_r4p0.ARPackage.instances())
				self.location.addItems(locations)
				savedLocation = self.settings.value('location', '')
				if savedLocation in locations:
					self.location.setCurrentIndex(locations.index(savedLocation))
				layout.addWidget(self.location)
				
				# Create a QLabel as the title for the combobox
				self.moduleLabel = QLabel('Choose a BSW module')
				layout.addWidget(self.moduleLabel)
				
				# Create a QComboBox and add some items
				self.module = QComboBox()
				self.module.addItems(sorted(p.__str__() for p in autosar_r4p0.BswImplementation.instances()))
				layout.addWidget(self.module)

				# Create QDialogButtonBox with OK and Cancel buttons
				self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
				
				# Connect the signals to the slots
				self.buttons.accepted.connect(self.accept)
				self.buttons.rejected.connect(self.reject)
				
				# Add the button box to the layout
				layout.addWidget(self.buttons)
				
				# Set the dialog layout
				self.setLayout(layout)
		# Create and show the dialog
		dialog = BswDialog()
		if dialog.exec():
			module = eval('autosar' + dialog.module.currentText().replace('/', '.'))
			location = eval('autosar' + dialog.location.currentText().replace('/', '.')).editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None)
			parent = self.autosarmodel.automat2model(location.model())
			if hasattr(parent, module.shortName.val()):
				return
			for child in parent._modelchildren:
				if child.name() == 'element':
					self.autosarmodel.automat2model(child)
					break
			root = location.element.EcucModuleConfigurationValues[module.shortName.val()]
			root.definition = module.vendorSpecificModuleDef[0].ref()
			root.moduleDescription = module
			self.autosarmodel._addedModel(root.model())
			module_location = root.model().__str__()
			self.bswselector.addItem(module_location)
			self.bswselector.setCurrentText(module_location)
			dialog.settings.setValue('location', dialog.location.currentText())
			dialog.settings.sync()

	def __init__(self):
		global mainWindow
		mainWindow = self
		QMainWindow.__init__(self)
		settings=QSettings()
		settings.beginGroup('MainWindow')
		if settings.contains("geometry"):
			self.restoreGeometry(settings.value("geometry"))
		else:
			self.resize(QScreen().availableGeometry().size() * 0.7)
			self.move(QPoint(200, 200))
		self.setWindowTitle('GoMAT')
		self._docked=[]
		
		#QMenuBar()
		#filemenu=self.menuBar().addMenu('File')
		#projmenu=self.menuBar().addMenu('Project')
		#editprojaction=projmenu.addAction('edit')
		
		#toolbar
		toolbar = QToolBar(self)
		toolbar.setObjectName('toolbar')
		action_save = QAction('save',toolbar)
		action_save.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_DialogSaveButton))
		action_save.triggered.connect(lambda:autosar.editor(file=mainWindow.fileselector.currentText() if mainWindow.fileselector.currentText() else None).save())
		toolbar.addAction(action_save)
		
		self.action_showNonExisting = QAction("show non-existing", toolbar)
		self.action_showNonExisting.setCheckable(True)
		self.action_showNonExisting.setChecked(settings.value('show non-existing', True, bool))
		self.action_showNonExisting.setToolTip('Check to show non-existing subcontainers')
		#action_showNonExisting.triggered.connect(self.existing_changed)
		toolbar.addAction(self.action_showNonExisting)

		self.action_properties = QAction("show properties", toolbar)
		self.action_properties.setCheckable(True)
		self.action_properties.setChecked(settings.value('show properties', True, bool))
		self.action_properties.setToolTip('Check to show the properties view')
		toolbar.addAction(self.action_properties)

		global action_showHex
		action_showHex = QAction("hex", toolbar)
		action_showHex.setCheckable(True)
		action_showHex.setChecked(settings.value('hex', True, bool))
		action_showHex.setToolTip('Check to show integers as hex')
		toolbar.addAction(action_showHex)
  # action_bswMode = QAction("BSW Mode", toolbar)
  # action_bswMode.setCheckable(True)
  # action_bswMode.setChecked(settings.value('BSW Mode',True,bool))
  # #action_bswMode.triggered.connect(self.existing_changed)
  # toolbar.addAction(action_bswMode)
  # self.action_bswMode=action_bswMode
		
		self.fileselector = QComboBox(toolbar)
		palette = self.fileselector.palette()
		palette.setColor(QPalette.Text, app.palette().color(QPalette.Highlight))
		self.fileselector.setPalette(palette)
		self.fileselector.addItem('')
		self.fileselector.addItems(sorted(autosar.file()))
		self.fileselector.setCurrentText(settings.value('selected file', '', str))
		toolbar.addWidget(self.fileselector)

		action_new_file = QAction('+',toolbar)
		#action_new_file.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon))
		font = QFont()
		font.setBold(True)
		font.setPointSize(12);
		action_new_file.setFont(font)
		action_new_file.triggered.connect(mainWindow.create_new_file)
		action_new_file.setToolTip('Create a new file for configuration data')
		toolbar.addAction(action_new_file)

		self.bswselector = QComboBox(toolbar)
		self.bswselector.addItems(sorted(s.__str__() for s in autosar_r4p0.EcucModuleConfigurationValues.instances()))
		self.bswselector.setCurrentText(settings.value('selected bsw', self.bswselector.itemText(0), str))
		toolbar.addWidget(self.bswselector)

		action_new_bsw_module = QAction('+',toolbar)
		#action_new_file.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon))
		font = QFont()
		font.setBold(True)
		font.setPointSize(12);
		action_new_bsw_module.setFont(font)
		action_new_bsw_module.triggered.connect(mainWindow.create_new_bsw)
		action_new_bsw_module.setToolTip('Add a new BSW module configuration')
		toolbar.addAction(action_new_bsw_module)
		# action_showAttributes = QAction("show attributes", toolbar)
		# action_showAttributes.setCheckable(True)
		# action_showAttributes.setChecked(settings.value('show attributes',True,bool))
		#action_showAttributes.triggered.connect(self.existing_changed)
		# toolbar.addAction(action_showAttributes)
		# self.action_showAttributes=action_showAttributes

		self.addToolBar(toolbar)
		
		self.setTabPosition( Qt.AllDockWidgetAreas , QTabWidget.North )
		self.setDockOptions( QMainWindow.AllowNestedDocks | QMainWindow.AllowTabbedDocks | QMainWindow.GroupedDragging)
		
		model = Model(self)
		self.autosarmodel = model
		mainwindow = self
		def ReReadModel():
			model.layoutAboutToBeChanged.emit()
			model.layoutChanged.emit()
		action_showHex.changed.connect(ReReadModel)
		self.fileselector.currentTextChanged.connect(ReReadModel)

		def addmerged():
			settings = QSettings()
			settings.beginGroup('MainWindow')
			settings.beginGroup('AutoSAR Model view')

			class MyQSortFilterProxyModel(QSortFilterProxyModel):

				def filterAcceptsRow(self, source_row:int, source_parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> bool:
					if not mainwindow.action_showNonExisting.isChecked() and self.sourceModel().data(self.sourceModel().index(source_row, 0, source_parent), Qt.OkToHideRole):
						return False
					if not mainwindow.action_properties.isChecked() and self.sourceModel().data(self.sourceModel().index(source_row, 1, source_parent), Qt.DisplayRole) is not None:
						return False
					return True#not mainwindow.fileselector.currentText() or mainwindow.fileselector.currentText() in self.sourceModel().data(self.sourceModel().index(source_row, 3, source_parent), Qt.DisplayRole)

			filterModel = MyQSortFilterProxyModel()
			filterModel.setSourceModel(model)
			# filterModel.filterAcceptsRow = lambda source_row, source_parent: filterModel.data(filterModel.index(source_row, 0, source_parent), Qt.ForegroundRole) is not None
			self._mergedview = ModelView(self, filterModel, settings)
			# view.setSortingEnabled(True)
			self.mergedModelView = DockWidget('AutoSAR Model view', self._mergedview)
			self.addDockWidget(Qt.TopDockWidgetArea, self.mergedModelView)
			self.action_showNonExisting.changed.connect(lambda: filterModel.invalidateFilter())
			self.action_properties.changed.connect(lambda: filterModel.invalidateFilter() or self._mergedview.setColumnHidden(1, not self.action_properties.isChecked()))
			self.fileselector.currentTextChanged.connect(lambda: filterModel.invalidate())
			self._docked.append(self.mergedModelView)
			self.mergedModelView.dockLocationChanged.connect(self.dockWidgetChanged)

		addmerged()

		def addBasMAT():

			class BasMATFilterProxyModel(QSortFilterProxyModel):
				_sourceRoot = QModelIndex()
				_row = 0

				def filterAcceptsRow(self, source_row:int, source_parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> bool:
					if source_parent == self._sourceRoot:
						 return source_row == self._row
					if not mainwindow.action_showNonExisting.isChecked() and self.sourceModel().data(self.sourceModel().index(source_row, 0, source_parent), Qt.OkToHideRole):
					 	return False
					if not mainwindow.action_properties.isChecked() and self.sourceModel().data(self.sourceModel().index(source_row, 1, source_parent), Qt.DisplayRole) is not None:
						return False
					return True

				def setRoot(self, index, view):
					self.beginResetModel()
					self._row = index.row()
					self._sourceRoot = index.parent()
					self.endResetModel()
					view.setRootIndex(self.mapFromSource(self._sourceRoot))
					view.expandToDepth(1)

			settings = QSettings()
			settings.beginGroup('MainWindow')
			settings.beginGroup('BasMAT Model view')
			proxyModel = ProxyModel(model)
			basmatModel = BasMATFilterProxyModel(proxyModel)
			basmatModel.setSourceModel(proxyModel)
			self.basmatView = ModelView(self, basmatModel, settings)
			def ChangeBasMatRoot(toPath):
				automat=eval('autosar' + toPath.replace('/', '.'))
				index=proxyModel.automat2index(automat)
				basmatModel.setRoot(index, self.basmatView)
			self.bswselector.currentTextChanged.connect(ChangeBasMatRoot)
			if self.bswselector.currentText():
				self.bswselector.currentTextChanged.emit(self.bswselector.currentText())
			self.bswselector.currentTextChanged.connect(lambda t: self.basmatView._restoreSettings())
			self.basmatModelView = DockWidget('BasMAT Model view', self.basmatView)
			self.addDockWidget(Qt.TopDockWidgetArea, self.basmatModelView)
			self.action_showNonExisting.changed.connect(lambda: basmatModel.invalidateFilter())
			self.action_properties.changed.connect(lambda: basmatModel.invalidateFilter() or self.basmatView.setColumnHidden(1, not self.action_properties.isChecked()))
			self.fileselector.currentTextChanged.connect(lambda: basmatModel.invalidate())
			self._docked.append(self.basmatModelView)
			self.basmatModelView.dockLocationChanged.connect(self.dockWidgetChanged)

		addBasMAT()

		def addProperty():

			class PropertyFilterProxyModel(QSortFilterProxyModel):
				_sourceRoot = QModelIndex()
				def filterAcceptsRow(self, source_row:int, source_parent:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> bool:
					return self.sourceModel().data(self.sourceModel().index(source_row, 1, source_parent), Qt.DisplayRole) is not None

				def mapFromSource(self, sourceIndex:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> PySide6.QtCore.QModelIndex:
					if sourceIndex == self._sourceRoot:
					 	return QModelIndex()
					return QSortFilterProxyModel.mapFromSource(self, sourceIndex)

				def mapToSource(self, proxyIndex:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> PySide6.QtCore.QModelIndex:
					if not proxyIndex.isValid():
						return self._sourceRoot
					return QSortFilterProxyModel.mapToSource(self, proxyIndex)

				def data(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex], role:int=...) -> Any:
					if role == Qt.ForegroundRole:
						automat = QSortFilterProxyModel.data(self, index, role=Qt.AutoMATRole)
						if not automat:
							return None
					if role==Qt.OkToHideRole:
						return False
					return QSortFilterProxyModel.data(self, index, role=role)

				def flags(self, index:Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex]) -> PySide6.QtCore.Qt.ItemFlags:
					if mainwindow.fileselector.currentText():
						automat = QSortFilterProxyModel.data(self, index, role=Qt.AutoMATRole)
						if hasattr(automat, 'file'):
							files = automat.file()
						else:
							files = set()
							for child in automat:
								files.update(child.file())
						if  automat and (len(files) > 1 or mainwindow.fileselector.currentText() not in files):
							return Qt.NoItemFlags
					return QSortFilterProxyModel.flags(self, index)

				def setRoot(self, model, index):
					self._view._saveSettings()
					#self.beginResetModel()
					if model == self.sourceModel():
						if index == self._sourceRoot:
							return
						# call setSourceModel with dummy, otherwise will the update call be ignored
						self.setSourceModel(None)
					self._sourceRoot = index
					self.setSourceModel(model)
					# self.sourceModelChanged.emit()
					#self.endResetModel()
					self._view._restoreSettings()
					if model:
						self._view.setWindowTitle('Property view: '+model.data(index, Qt.AutoMATRole).__str__())
					else:
						self._view.setWindowTitle('Property view')

			settings = QSettings()
			settings.beginGroup('MainWindow')
			settings.beginGroup('Property view')
			self.propertyModel = PropertyFilterProxyModel(self)
			view = ModelView(self, self.propertyModel, settings)
			self.propertyModel._view = view;
			self.propertyModelView = DockWidget('Property view', view)
			self.addDockWidget(Qt.BottomDockWidgetArea, self.propertyModelView)
			self._mergedview._refModel = self.propertyModel
			self.basmatView._refModel = self.propertyModel
			self._docked.append(self.propertyModelView)
			self.propertyModelView.dockLocationChanged.connect(self.dockWidgetChanged)
			self.action_properties.changed.connect(lambda: self.propertyModelView.setVisible(not self.action_properties.isChecked()))

			self.bswselector.currentTextChanged.connect(lambda: self.propertyModel.setRoot(None, QModelIndex()))
			self.action_showNonExisting.changed.connect(lambda: self.propertyModel.setRoot(None, QModelIndex()))
			self.fileselector.currentTextChanged.connect(lambda: self.propertyModel.invalidate())

		addProperty()
		self.action_properties.changed.emit()
		if settings.contains("windowState"):
			self.restoreState(settings.value("windowState"))
	
	def dockWidgetChanged(self):
		for d in self._docked:
			if self.tabifiedDockWidgets(d):
				d.setTitleBarWidget(d._emptyTitleBar)
			else:
				d.setTitleBarWidget(None)
				
	def closeEvent(self, *args, **kwargs):
		settings=QSettings()
		settings.beginGroup('MainWindow')
		settings.setValue("geometry",self.saveGeometry())
		settings.setValue("windowState",self.saveState())
		settings.setValue('show non-existing', self.action_showNonExisting.isChecked())
		settings.setValue('show properties', self.action_properties.isChecked())
		settings.setValue('hex', action_showHex.isChecked())
		settings.setValue('selected file', self.fileselector.currentText())
		settings.setValue('selected bsw', self.bswselector.currentText())
		settings.endGroup()
		settings.sync()
		super().closeEvent(*args, **kwargs)
		self.mergedModelView.close()
		self.basmatModelView.close()
		self.propertyModelView.close()


app = QApplication([])

QApplication.setOrganizationName('AutoMAT')
QApplication.setApplicationName('GoMAT')

myappid = 'GoMAT' # arbitrary string
if platform.system() == "Windows":
	ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) # must tell windows that it should use my app icon and not python icon
app.setWindowIcon(QIcon(os.path.join(gomatpath,'automat.svg')))

QSettings.setDefaultFormat(QSettings.IniFormat)
mainWindow=MainWindow()
mainWindow.show()
sys.exit(app.exec())
